package svm_adag;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap;

import libsvm.svm_model;
import libsvm.svm_node;
import libsvm.svm_parameter;
import param.Kernel;

public class ADAG {

	public static double adag_predict_values(svm_model model, svm_node[] x, double[] dec_values) {
		int i;
		if (model.param.svm_type == svm_parameter.ONE_CLASS || model.param.svm_type == svm_parameter.EPSILON_SVR
				|| model.param.svm_type == svm_parameter.NU_SVR) {
			double[] sv_coef = model.sv_coef[0];
			double sum = 0;
			for (i = 0; i < model.l; i++)
				sum += sv_coef[i] * Kernel.k_function(x, model.SV[i], model.param);
			sum -= model.rho[0];
			dec_values[0] = sum;

			if (model.param.svm_type == svm_parameter.ONE_CLASS)
				return (sum > 0) ? 1 : -1;
			else
				return sum;
		} else {
			int nr_class = model.nr_class;
			int l = model.l;

			double[] kvalue = new double[l];
			for (i = 0; i < l; i++)
				kvalue[i] = Kernel.k_function(x, model.SV[i], model.param);

			int[] start = new int[nr_class];
			start[0] = 0;
			for (i = 1; i < nr_class; i++)
				start[i] = start[i - 1] + model.nSV[i - 1];

			TreeMap<Integer, Integer> hm = new TreeMap<>();
			hm.put(0, 0);
			for (i = 1; i < nr_class; i++) {
				hm.put(i, hm.get(i - 1) + model.nSV[i - 1]);
			}

			int[] vote = new int[nr_class];
			for (i = 0; i < nr_class; i++)
				vote[i] = 0;

			adag_recursive(hm, model, kvalue, dec_values, 0, vote);
			int vote_max_idx = 0;
			for (i = 1; i < nr_class; i++)
				if (vote[i] > vote[vote_max_idx])
					vote_max_idx = i;

			return model.label[vote_max_idx];
		}
	}

	public static double adag_predict(svm_model model, svm_node[] x) {
		int nr_class = model.nr_class;
		double[] dec_values;
		if (model.param.svm_type == svm_parameter.ONE_CLASS || model.param.svm_type == svm_parameter.EPSILON_SVR
				|| model.param.svm_type == svm_parameter.NU_SVR)
			dec_values = new double[1];
		else
			dec_values = new double[nr_class - 1];// s
		double pred_result = adag_predict_values(model, x, dec_values);
		return pred_result;
	}

	private static void adag_recursive(TreeMap<Integer, Integer> hm, svm_model model, double[] kvalue,
			double[] dec_values, int temp, int[] vote) {
		TreeMap<Integer, Integer> temp_hm = new TreeMap<>();
		Iterator<Entry<Integer, Integer>> iterator = hm.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<Integer, Integer> entry1 = iterator.next();
			if (iterator.hasNext()) {
				temp++;
				Entry<Integer, Integer> entry2 = iterator.next();
				// System.out.println(entry1.getKey());
				// System.out.println(entry2.getKey());
				int p = (entry2.getKey() - entry1.getKey() - 1) + model.nr_class * entry1.getKey() - entry1.getKey()
						* (entry1.getKey() + 1) / 2;
				double sum = 0;
				int si = entry1.getValue();
				int sj = entry2.getValue();
				int ci = model.nSV[entry1.getKey()];
				int cj = model.nSV[entry2.getKey()];

				int k;
				double[] coef1 = model.sv_coef[entry2.getKey() - 1];
				double[] coef2 = model.sv_coef[entry1.getKey()];
				for (k = 0; k < ci; k++)
					sum += coef1[si + k] * kvalue[si + k];
				for (k = 0; k < cj; k++)
					sum += coef2[sj + k] * kvalue[sj + k];
				sum -= model.rho[p];
				dec_values[temp - 1] = sum;
				if (dec_values[temp - 1] > 0) {
					// ++vote[entry1.getKey()];
					temp_hm.put(entry1.getKey(), entry1.getValue());
				} else {
					// ++vote[entry2.getKey()];
					temp_hm.put(entry2.getKey(), entry2.getValue());
				}
			} else {
				temp_hm.put(entry1.getKey(), entry1.getValue());
			}
		}
		if (temp_hm.size() > 1) {
			adag_recursive(temp_hm, model, kvalue, dec_values, temp, vote);
		} else {
			Iterator<Entry<Integer, Integer>> iter = temp_hm.entrySet().iterator();
			if (iter.hasNext()) {
				Entry<Integer, Integer> entry = iter.next();
				++vote[entry.getKey()];
			}
		}
	}

}
